/*************************************************************************** 
 * (c) 2006 Dmitry Osinovsky, osdmsoftware@gmail.com
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU Lesser General Public License as        *
 *   published by the Free Software Foundation; either version 2 of the    *
 *   License, or (at your option) any later version.                       *
 *                                                                         *
 ***************************************************************************/

using System;
using System.Collections.Generic;
using System.Text;
using System.Drawing;
using System.Diagnostics;
using System.Windows.Forms;
using System.Drawing.Imaging;
using System.Resources;

namespace OsdmSoftware.PocketPicture
{
    public class SelectionTool: PaintTool
    {
        public SelectionTool()
            : base("Select selection tool and open selection menu")
        {
            CreateChildMenu = true;
            //this.ShowImagesInDropDown = false;
            this.DisplayCheckBoxIfNoImage = false;
        }

        public enum StateType { NoSelection, HasFirstPoint, MoveSelection, CopySelection, ResizeSelection };
        StateType state = StateType.NoSelection;

        string GetSelectionDesc()
        {
            return "(" + Form.BitmapZoomer.SelectionRect.X.ToString() + ", " +
                Form.BitmapZoomer.SelectionRect.Y.ToString() + "), " +
                Form.BitmapZoomer.SelectionRect.Width.ToString() +  "x" +
                Form.BitmapZoomer.SelectionRect.Height.ToString() + " ";
        }

        public override bool TemporaryTool()
        {
            return true;
        }

        public StateType State
        {
            get
            {
                return state;
            }
            set
            {
                if (state == StateType.ResizeSelection &&
                    (value == StateType.MoveSelection || value == StateType.CopySelection))
                {
                    selection.Dispose();
                    selection = new Bitmap(Form.BitmapZoomer.SelectionRect.Width, Form.BitmapZoomer.SelectionRect.Height, PixelFormat.Format32bppRgb);
                    Graphics gr = Graphics.FromImage(selection);
                    gr.DrawImage(Form.BitmapZoomer.Bitmap, new Rectangle(0, 0, Form.BitmapZoomer.SelectionRect.Width, Form.BitmapZoomer.SelectionRect.Height), Form.BitmapZoomer.SelectionRect, GraphicsUnit.Pixel);
                    gr.Dispose();
                }

                if (value == StateType.NoSelection)
                {
                    Form.HintLabel.Text = "Select first point";
                }
                else if (value == StateType.HasFirstPoint)
                {
                    Trace.Assert(Form.BitmapZoomer.SelectedPoints.Count == 1, "Must be one and only one selected point");
                    Point p = Form.BitmapZoomer.SelectedPoints[0];
                    Form.HintLabel.Text = "(" + p.X.ToString() + ", " + p.Y.ToString() + "). Select second point";
                }
                else if (value == StateType.MoveSelection)
                {
                    Form.HintLabel.Text = GetSelectionDesc() + "Select background color and move selection";
                }
                else if (value == StateType.CopySelection)
                {
                    Form.HintLabel.Text = GetSelectionDesc() + "Select background color and copy selection";
                }
                else if (value == StateType.ResizeSelection)
                {
                    Form.HintLabel.Text = GetSelectionDesc() + "Select background color and resize selection";
                }

                state = value;
            }
        }

        Bitmap oldBitmap;
        Bitmap selection;

        public Bitmap Selection { get { return selection; } set { selection = value; } }

        public override void SelectTool()
        {
            State = StateType.NoSelection;
            if (oldBitmap != null)
                oldBitmap.Dispose();
            oldBitmap = null;
            if (selection != null)
                selection.Dispose();
            selection = null;
        }

        public void ClearSelection(Graphics gr)
        {
            if (oldBitmap != null)
            {
                gr.DrawImage(oldBitmap, new Rectangle(Form.BitmapZoomer.SelectionRect.X, Form.BitmapZoomer.SelectionRect.Y, oldBitmap.Width, oldBitmap.Height),
                    new Rectangle(0, 0, oldBitmap.Width, oldBitmap.Height), GraphicsUnit.Pixel);
                oldBitmap.Dispose();
                oldBitmap = null;
            }
            else
                gr.FillRectangle(new SolidBrush(Form.PaletteSelectedColor), Form.BitmapZoomer.SelectionRect);
        }

        int xoffset;
        int yoffset;

        public override void MouseDown(int x, int y)
        {
            if (state == StateType.NoSelection)
            {
                Form.BitmapZoomer.AddSelectedPoint(x, y);
                State = StateType.HasFirstPoint;
            }
            else if (state == StateType.MoveSelection || state == StateType.CopySelection)
            {
                if (x >= Form.BitmapZoomer.SelectionRect.Left && y >= Form.BitmapZoomer.SelectionRect.Top &&
                    x < Form.BitmapZoomer.SelectionRect.Right && y < Form.BitmapZoomer.SelectionRect.Bottom)
                {
                    xoffset = x - Form.BitmapZoomer.SelectionRect.Left;
                    yoffset = y - Form.BitmapZoomer.SelectionRect.Top;
                    CopyOrMoveSelection(x - xoffset, y - yoffset);
                }
                else
                {
                    Cancel();
                    Form.BitmapZoomer.AddSelectedPoint(x, y);
                    State = StateType.HasFirstPoint;
                }
            }
            else if (state == StateType.ResizeSelection)
            {
                ResizeSelection(x, y);
            }
        }

        public void CopyOrMoveSelection(int x, int y)
        {
            Rectangle newSelectionRect = new Rectangle(x, y, Form.BitmapZoomer.SelectionRect.Width, Form.BitmapZoomer.SelectionRect.Height);
            Graphics gr = Graphics.FromImage(Form.BitmapZoomer.Bitmap);

            if (state == StateType.MoveSelection)
            {
                ClearSelection(gr);
            }

            oldBitmap = new Bitmap(Form.BitmapZoomer.SelectionRect.Width, Form.BitmapZoomer.SelectionRect.Height, PixelFormat.Format32bppRgb);
            Graphics gr1 = Graphics.FromImage(oldBitmap);
            gr1.DrawImage(Form.BitmapZoomer.Bitmap, new Rectangle(0, 0, newSelectionRect.Width, newSelectionRect.Height), newSelectionRect, GraphicsUnit.Pixel);
            gr1.Dispose();

            ImageAttributes attrs = new ImageAttributes();
            if (Form.CurrentColorIsTransparentColorForSelection)
                attrs.SetColorKey(Form.PaletteSelectedColor, Form.PaletteSelectedColor);
            gr.DrawImage(selection, newSelectionRect,
                0, 0, selection.Width, selection.Height, GraphicsUnit.Pixel, attrs);
            gr.Dispose();
            int x1 = Math.Min(Form.BitmapZoomer.SelectionRect.Left, newSelectionRect.Left);
            int y1 = Math.Min(Form.BitmapZoomer.SelectionRect.Top, newSelectionRect.Top);
            int x2 = Math.Max(Form.BitmapZoomer.SelectionRect.Right, newSelectionRect.Right);
            int y2 = Math.Max(Form.BitmapZoomer.SelectionRect.Bottom, newSelectionRect.Bottom);
            Form.BitmapZoomer.SelectionRect = newSelectionRect;
            Form.BitmapZoomer.InvalidateBitmap(x1, y1, x2 - x1, y2 - y1, true);

            State = StateType.MoveSelection;
        }

        void ResizeSelection(int x, int y)
        {
            if (x < Form.BitmapZoomer.SelectionRect.X || y < Form.BitmapZoomer.SelectionRect.Y)
            {
                return;
            }

            Rectangle newSelectionRect = new Rectangle(
                Form.BitmapZoomer.SelectionRect.X, Form.BitmapZoomer.SelectionRect.Y,
                x + 1 - Form.BitmapZoomer.SelectionRect.X,
                y + 1 - Form.BitmapZoomer.SelectionRect.Y);

            Graphics gr = Graphics.FromImage(Form.BitmapZoomer.Bitmap);
            ClearSelection(gr);

            oldBitmap = new Bitmap(newSelectionRect.Width, newSelectionRect.Height, PixelFormat.Format32bppRgb);
            Graphics gr1 = Graphics.FromImage(oldBitmap);
            gr1.DrawImage(Form.BitmapZoomer.Bitmap, new Rectangle(0, 0, newSelectionRect.Width, newSelectionRect.Height), newSelectionRect, GraphicsUnit.Pixel);
            gr1.Dispose();

            ImageAttributes attrs = new ImageAttributes();
            if (Form.CurrentColorIsTransparentColorForSelection)
                attrs.SetColorKey(Form.PaletteSelectedColor, Form.PaletteSelectedColor);
            gr.DrawImage(selection, newSelectionRect,
                0, 0, selection.Width, selection.Height,
                GraphicsUnit.Pixel, attrs);
            gr.Dispose();
            int x1 = Math.Min(Form.BitmapZoomer.SelectionRect.Left, newSelectionRect.Left);
            int y1 = Math.Min(Form.BitmapZoomer.SelectionRect.Top, newSelectionRect.Top);
            int x2 = Math.Max(Form.BitmapZoomer.SelectionRect.Right, newSelectionRect.Right);
            int y2 = Math.Max(Form.BitmapZoomer.SelectionRect.Bottom, newSelectionRect.Bottom);
            Form.BitmapZoomer.SelectionRect = newSelectionRect;
            Form.BitmapZoomer.InvalidateBitmap(x1, y1, x2 - x1, y2 - y1, true);
        }

        public void FillSelection()
        {
            selection = new Bitmap(Form.BitmapZoomer.SelectionRect.Width, Form.BitmapZoomer.SelectionRect.Height, PixelFormat.Format32bppRgb);
            Graphics gr = Graphics.FromImage(selection);
            gr.DrawImage(Form.BitmapZoomer.Bitmap, new Rectangle(0, 0, Form.BitmapZoomer.SelectionRect.Width, Form.BitmapZoomer.SelectionRect.Height), Form.BitmapZoomer.SelectionRect, GraphicsUnit.Pixel);
            gr.Dispose();

            Form.BitmapZoomer.SelectedPoints.Clear();
            State = StateType.MoveSelection;
        }

        public override void MouseUp()
        {
            if (state == StateType.HasFirstPoint)
            {
                if (Form.BitmapZoomer.SelectionRect.Width == 0 || Form.BitmapZoomer.SelectionRect.Height == 0)
                {
                    Cancel();
                    return;
                }

                FillSelection();
            }
            else if (state == StateType.MoveSelection || state == StateType.ResizeSelection)
            {
                /*selection = new Bitmap(Form.BitmapZoomer.SelectionRect.Width, Form.BitmapZoomer.SelectionRect.Height, PixelFormat.Format32bppRgb);
                Graphics gr = Graphics.FromImage(selection);
                gr.DrawImage(Form.BitmapZoomer.Bitmap, 0, 0, Form.BitmapZoomer.SelectionRect, GraphicsUnit.Pixel); // wrong
                gr.Dispose();
                if (oldBitmap != null)
                {
                    oldBitmap.Dispose();
                    oldBitmap = null;
                }*/

                Form.PushToUndo();
                if (state == StateType.ResizeSelection)
                    State = StateType.MoveSelection;
            }
        }

        public override void MouseMove(int x, int y)
        {
            if (state == StateType.HasFirstPoint)
            {
                Trace.Assert(Form.BitmapZoomer.SelectedPoints.Count == 1, "Must be one and only one selected point");
                Point p = Form.BitmapZoomer.SelectedPoints[0];
                int newx = Math.Min(p.X, x);
                int newy = Math.Min(p.Y, y);
                int w = Math.Abs(p.X - x) + 1;
                int h = Math.Abs(p.Y - y) + 1;
                Rectangle newSelectionRect = new Rectangle(newx, newy, w, h);
                int x1 = newSelectionRect.Left;
                int y1 = newSelectionRect.Top;
                int x2 = newSelectionRect.Right;
                int y2 = newSelectionRect.Bottom;
                if (Form.BitmapZoomer.SelectionRect.Width != 0 && Form.BitmapZoomer.SelectionRect.Height != 0)
                {
                    x1 = Math.Min(Form.BitmapZoomer.SelectionRect.Left, x1);
                    y1 = Math.Min(Form.BitmapZoomer.SelectionRect.Top, y1);
                    x2 = Math.Max(Form.BitmapZoomer.SelectionRect.Right, x2);
                    y2 = Math.Max(Form.BitmapZoomer.SelectionRect.Bottom, y2);
                }
                /*Form.HintLabel.Text = "Was " + Form.BitmapZoomer.SelectionRect.Right.ToString() + " is " + 
                    newSelectionRect.Right.ToString();*/
                Form.BitmapZoomer.SelectionRect = newSelectionRect;
                Form.BitmapZoomer.InvalidateBitmap(x1, y1, x2 - x1, y2 - y1, true);
                //Form.BitmapZoomer.InvalidateBitmap(x1 - 1, y1 - 1, w + 3, h + 3);
                Form.HintLabel.Text = GetSelectionDesc();
                Form.HintLabel.Refresh();
            }
            else if (state == StateType.MoveSelection || state == StateType.CopySelection)
            {
                CopyOrMoveSelection(x - xoffset, y - yoffset);
            }
            else if (state == StateType.ResizeSelection)
            {
                ResizeSelection(x, y);
            }
        }

        public override void DoAction()
        {
            Form.ShowMenus(new ToolMenu[] { Form.Selection1, Form.Selection2/*, Form.Selection3*/ });
            Form.SetActiveMainMenuItem(this);
            CloseDropDownOnClick = true;
            if (Form.Mode == this && State == StateType.ResizeSelection)
            {
                State = StateType.MoveSelection;
            }
            /*else if (Form.Mode == this && State != StateType.NoSelection)
            {
                Element.Panel.DisplayDropDownMenu(Element);
                CloseDropDownOnClick = false;
            }
            else*/
            base.DoAction();
        }

        public override void Cancel()
        {
            if (state == StateType.HasFirstPoint)
            {
                Form.BitmapZoomer.SelectedPoints.Clear();
                Form.BitmapZoomer.Refresh();
                SelectTool();
            }
            else if (state == StateType.MoveSelection || state == StateType.CopySelection || state == StateType.ResizeSelection)
            {
                Form.BitmapZoomer.SelectionRect = Rectangle.Empty;
                Form.BitmapZoomer.Refresh();
                SelectTool();
            }

            base.Cancel();
        }

        public override bool CanCancel()
        {
            return state != StateType.NoSelection;
        }

        public void SelectAll()
        {
            Form.BitmapZoomer.SelectionRect = new Rectangle(0, 0, Form.BitmapZoomer.Bitmap.Width, Form.BitmapZoomer.Bitmap.Height);
            FillSelection();
            Form.BitmapZoomer.InvalidateBitmap(0, 0, Form.BitmapZoomer.Bitmap.Width, Form.BitmapZoomer.Bitmap.Height, true);
        }
    }

    public class SelectAllAction: Action
    {
        public SelectAllAction()
        {
            ParentName = "Selection2";
            this.Name = "All";
            this.ImageName = "SelectAll";
            this.Hint = "Select all";
        }

        public override void DoAction()
        {
            SelectionTool st = Form.SelectionTool;
            /*Form.BitmapZoomer.SelectionRect = new Rectangle(0, 0, Form.BitmapZoomer.Bitmap.Width, Form.BitmapZoomer.Bitmap.Height);
            st.FillSelection();
            Form.BitmapZoomer.InvalidateBitmap(0, 0, Form.BitmapZoomer.Bitmap.Width, Form.BitmapZoomer.Bitmap.Height, true);*/
            st.SelectAll();

            base.DoAction();
        }
    }

    public class CopySelectionAction: Action
    {
        public CopySelectionAction()
        {
            ParentName = "Selection1";
            this.Name = "Replicate selection";
            this.ImageName = "CopySelectionAction";
            this.Hint = "Duplicate selection";
        }

        public override void DoAction()
        {
            if (Form.Mode is SelectionTool)
            {
                SelectionTool st = (SelectionTool) Form.Mode;
                if (st.State == SelectionTool.StateType.NoSelection || st.State == SelectionTool.StateType.HasFirstPoint)
                {
                    MessageBox.Show("At first you must select an area");
                    return;
                }
                st.State = SelectionTool.StateType.CopySelection;
            }
            else
            {
                MessageBox.Show("At first you must select an area");
            }

            base.DoAction();
        }
    }

    public class ResizeSelectionAction: Action
    {
        public ResizeSelectionAction()
        {
            ParentName = "Selection1";
            this.Name = "Resize selection";
            this.ImageName = "ResizeSelectionAction";
            this.Hint = "Resize selected part of image";
        }

        public override void DoAction()
        {
            if (Form.Mode is SelectionTool)
            {
                SelectionTool st = (SelectionTool) Form.Mode;
                if (st.State == SelectionTool.StateType.NoSelection || st.State == SelectionTool.StateType.HasFirstPoint)
                {
                    MessageBox.Show("At first you must select an area");
                    return;
                }

                st.State = SelectionTool.StateType.ResizeSelection;
            }
            else
            {
                MessageBox.Show("At first you must select an area");
            }

            base.DoAction();
        }
    }

    public class CropSelectionAction: Action
    {
        public CropSelectionAction()
        {
            ParentName = "Selection1";
            this.Name = "Crop selection";
            this.ImageName = "CropSelectionAction";
            this.Hint = "Crop selection as whole image";
        }

        public override void DoAction()
        {
            if (Form.Mode is SelectionTool)
            {
                SelectionTool st = (SelectionTool) Form.Mode;
                if (st.State == SelectionTool.StateType.NoSelection || st.State == SelectionTool.StateType.HasFirstPoint)
                {
                    MessageBox.Show("At first you must select an area");
                    return;
                }
                st.State = SelectionTool.StateType.MoveSelection;
                Bitmap newBitmap = (Bitmap) st.Selection.Clone();
                st.Cancel();
                Form.BitmapZoomer.Bitmap.Dispose();
                Form.BitmapZoomer.Bitmap = newBitmap;
                Form.PushToUndo();
            }
            else
            {
                MessageBox.Show("At first you must select an area");
            }

            base.DoAction();
        }
    }

    public class CopyToClipboardAction: Action
    {
        public CopyToClipboardAction()
        {
            ParentName = "Selection1";
            this.Name = "Copy to clipboard";
            this.ImageName = "CopyAction";
            this.Hint = "Copy selection to clipboard";
        }

        public override void DoAction()
        {
            if (Form.Mode is SelectionTool)
            {
                SelectionTool st = (SelectionTool) Form.Mode;
                bool wasNoSelection = false;
                if (st.State == SelectionTool.StateType.NoSelection || st.State == SelectionTool.StateType.HasFirstPoint)
                {
                    st.SelectAll();
                    wasNoSelection = true;
                    //MessageBox.Show("At first you must select an area");
                    //return;
                }

                Bitmap intermediateBitmap = new Bitmap(st.Selection);
#if PocketPC
                Clipboard.SetDataObject(intermediateBitmap);
#else
                Clipboard.SetImage(intermediateBitmap);
#endif
                if (wasNoSelection)
                    st.Cancel();
            }
            else
            {
                MessageBox.Show("At first you must select an area");
            }

            base.DoAction();
        }
    }

    public class CutToClipboardAction: Action
    {
        public CutToClipboardAction()
        {
            ParentName = "Selection1";
            this.Name = "Cut to clipboard";
            this.ImageName = "CutAction";
            this.Hint = "Cut selection to clipboard";
        }

        public override void DoAction()
        {
            if (Form.Mode is SelectionTool)
            {
                SelectionTool st = (SelectionTool) Form.Mode;
                if (st.State == SelectionTool.StateType.NoSelection || st.State == SelectionTool.StateType.HasFirstPoint)
                {
                    MessageBox.Show("At first you must select an area");
                    return;
                }

                Bitmap intermediateBitmap = new Bitmap(st.Selection);
#if PocketPC
                Clipboard.SetDataObject(intermediateBitmap);
#else
                Clipboard.SetImage(intermediateBitmap);
#endif

                Graphics gr = Graphics.FromImage(Form.BitmapZoomer.Bitmap);
                st.ClearSelection(gr);
                gr.Dispose();
                Form.BitmapZoomer.Refresh();
                Form.PushToUndo();

                st.SelectTool();
            }
            else
            {
                MessageBox.Show("Select an area to cut");
            }

            base.DoAction();
        }
    }

    public class PasteFromClipboardAction: Action
    {
        public PasteFromClipboardAction()
        {
            ParentName = "Selection1";
            this.Name = "Paste from clipboard";
            this.ImageName = "PasteAction";
            this.Hint = "Paste image from clipboard as selection";
        }

        public override void DoAction()
        {
            SelectionTool st = Form.SelectionTool;
            IDataObject iData = Clipboard.GetDataObject();
 
            // Determines whether the data is in a format you can use.
            if(! iData.GetDataPresent(DataFormats.Bitmap))
            {
                MessageBox.Show("No bitmap in clipboard");
                return;
            }

            Image img = (Image) iData.GetData(DataFormats.Bitmap);
            Bitmap bmp = new Bitmap(img.Width, img.Height, PixelFormat.Format32bppRgb);
            Graphics gr = Graphics.FromImage(bmp);
            gr.DrawImage(img, new Rectangle(0, 0, img.Width, img.Height), new Rectangle(0, 0, img.Width, img.Height), GraphicsUnit.Pixel);
            gr.Dispose();
            
            Form.SetMode(st);
            st.State = SelectionTool.StateType.CopySelection;
            int selX = Form.BitmapZoomer.XScroll * 
                BitmapZoomer.ZoomFactorUnit / Form.BitmapZoomer.ZoomFactor;
            int selY = Form.BitmapZoomer.YScroll * 
                BitmapZoomer.ZoomFactorUnit / Form.BitmapZoomer.ZoomFactor;
            Form.BitmapZoomer.SelectionRect = new Rectangle(selX, selY, bmp.Width, bmp.Height);
            st.Selection = bmp;
            st.CopyOrMoveSelection(selX, selY);

            img.Dispose();
        }
    }

    public class TransparentSelectionAction: Action
    {
        public TransparentSelectionAction()
        {
            ParentName = "Selection2";
            this.Name = "Transparent selection";
            this.ImageName = "TransparentSelection";
            this.Hint = "When selecting area make pixels with current color transparent";
        }

        public override void DoAction()
        {
            Form.CurrentColorIsTransparentColorForSelection = !Form.CurrentColorIsTransparentColorForSelection;
            this.Element.IsPushed = Form.CurrentColorIsTransparentColorForSelection;
            this.Element.Refresh();
            //base.DoAction();
        }
    }
}